// Helper for version informations.

#include "VersionHelper.h"
#include <winver.h>

// Retrieves the version informations.
bool VersionHelper::GetVersionInformations(VersionInformations& versionInformations)
{
	wchar_t fileName[MAX_PATH] = {0};
	wchar_t prefix[26] = {0};
	LPVOID informations;
	DWORD fileNameSize = sizeof(fileName) / sizeof(*fileName);
	DWORD fileVersionInfoSize;
	HANDLE hMem;

	// try to retrieve the parameters
	if(!TryGetParameters(fileName, fileNameSize, fileVersionInfoSize, hMem, versionInformations)) return false;

	// try to retrieve the buffer, then the prefix, then the required informations
	bool result = TryGetBuffer(fileName, fileVersionInfoSize, hMem, informations) &&
		TryGetPrefix(informations, prefix) &&
		TryGetResource(informations, prefix, L"CompanyName", versionInformations.CompanyName) &&
		TryGetResource(informations, prefix, L"FileDescription", versionInformations.FileDescription) && 
		TryGetResource(informations, prefix, L"FileVersion", versionInformations.FileVersion) &&
		TryGetResource(informations, prefix, L"InternalName", versionInformations.InternalName) && 
		TryGetResource(informations, prefix, L"LegalCopyright", versionInformations.LegalCopyright) &&
		TryGetResource(informations, prefix, L"OriginalFilename", versionInformations.OriginalFilename) && 
		TryGetResource(informations, prefix, L"ProductName", versionInformations.ProductName) &&
		TryGetResource(informations, prefix, L"ProductVersion", versionInformations.ProductVersion);

	// try to free the memory
	if (GlobalFree(hMem) != NULL) return LogManager::DisplayWarning(L"Could not free the specified global memory object.\n");

	return result;
}

// Try to retrieves the version informations parameters.
bool VersionHelper::TryGetParameters(wchar_t* fileName, const DWORD& fileNameSize, DWORD& fileVersionInfoSize, HANDLE& hMem, VersionInformations& versionInformations)
{
	if(GetModuleFileNameW(NULL, fileName, fileNameSize) == fileNameSize) return LogManager::DisplayWarning(L"The executable path is too long.\n");
	versionInformations.CurrentExePath = _N(fileName);

	DWORD handle = 0;
	if ((fileVersionInfoSize = GetFileVersionInfoSizeW(fileName, &handle)) == 0) return LogManager::DisplayWarning(L"Could not retrieve version information size.\n");

	if ((hMem = GlobalAlloc(GMEM_MOVEABLE, fileVersionInfoSize)) == NULL) return LogManager::DisplayWarning(L"Could not allocate enough bytes from the heap.\n");
	return true;
}

// Try to retrieves the version informations buffer.
bool VersionHelper::TryGetBuffer(const wchar_t* fileName, const DWORD& fileVersionInfoSize, const HANDLE& hMem, LPVOID& versionInformations)
{
	DWORD handle = 0;
	if ((versionInformations = GlobalLock(hMem)) == NULL) return LogManager::DisplayWarning(L"Could not lock a global memory object.\n");
	if (GetFileVersionInfoW(fileName, handle, fileVersionInfoSize, versionInformations) == 0) return LogManager::DisplayWarning(L"Could not retrieve version information.\n");
	return true;
}

// Try to retrieves the version informations resources prefix.
bool VersionHelper::TryGetPrefix(LPVOID& versionInformations, wchar_t* result)
{
	DWORD* buffer;
	UINT dataLength = 0;
	if (VerQueryValueW(versionInformations, L"\\VarFileInfo\\Translation", (LPVOID*)&buffer, &dataLength) == 0 || dataLength == 0) return LogManager::DisplayWarning(L"Could not retrieve the version information translations.\n");
	if (swprintf_s(result, 26, L"\\StringFileInfo\\%04x%04x\\", (WORD)*buffer, (WORD)(*buffer >> 16)) == -1) return LogManager::DisplayWarning(L"Could not write formatted data.\n");
	return true;
}

// Try to retrieves the specified version informations resource.
bool VersionHelper::TryGetResource(LPVOID& buffer, const wchar_t* prefix, const wchar_t* resourceName, string& result)
{
	wchar_t resource[50] = {0};
	LPCWSTR value = NULL;
	UINT dataLength = 0;
	if (swprintf_s(resource, L"%ls%ls", prefix, resourceName) == -1) return LogManager::DisplayWarning(L"Could not write formatted data.\n");
	if (VerQueryValueW(buffer, resource, (LPVOID*)&value, &dataLength) == 0 || dataLength == 0) return LogManager::DisplayWarning(L"Could not retrieve the version information value of %ls.\n", resourceName);
	result = _N(value);
	return true;
}